// $Id: stream.c 72 2011-04-24 05:05:11Z cedric.shih@gmail.com $
/*
 * Copyright (C) 2011 Cedric Shih <cedric.shih@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <niohttp.h>

#include "log.h"

#include "stream.h"

struct niohttpd_streams *niohttpd_streams_new(void)
{
	struct niohttpd_streams *streams;

	if (!(streams = (struct niohttpd_streams *)calloc(
			1, sizeof(*streams)))) {
		niohttpd_error("calloc: %s", strerror(errno));
		goto finally;
	}

	TAILQ_INIT(&streams->list);
	RB_INIT(&streams->tree);

finally:
	return streams;
}

void niohttpd_streams_free(struct niohttpd_streams *streams)
{
	if (!streams) {
		return;
	}

	niohttpd_streams_clear(streams);
	free(streams);
}

struct niohttpd_stream_entry *niohttpd_streams_find(
		struct niohttpd_streams *streams, const char *uri)
{
	struct niohttpd_stream_entry find;

	find.uri = (char *)uri;
	return RB_FIND(niohttpd_stream_tree, &streams->tree, &find);
}

inline struct niohttpd_stream_entry *niohttpd_streams_match(
		struct niohttpd_streams *streams, const char *uri)
{
	return niohttpd_streams_find(streams, uri);
}

int niohttpd_streams_add(struct niohttpd_streams *streams,
		const char *uri, struct niohttpd_stream_entry **out)
{
	int rc;
	struct niohttpd_stream_entry *entry;

	if ((entry = niohttpd_streams_find(streams, uri))) {
		niohttpd_error("exists: %s", uri);
		rc = EEXIST;
		goto finally;
	}

	if (!(entry = (struct niohttpd_stream_entry *)
			calloc(1, sizeof(*entry)))) {
		rc = errno ? errno : -1;
		niohttpd_error("calloc: %s", strerror(rc));
		goto finally;
	}

	if (!(entry->uri = strdup(uri))) {
		rc = errno ? errno : -1;
		niohttpd_error("strdup: %s", strerror(rc));
		free(entry);
		goto finally;
	}

	TAILQ_INIT(&entry->clients);
	RB_INSERT(niohttpd_stream_tree, &streams->tree, entry);

	entry->stream.uri = uri;
	TAILQ_INSERT_TAIL(&streams->list, &entry->stream, next);

	*out = entry;
	rc = 0;

finally:
	return rc;
}

void niohttpd_streams_remove(struct niohttpd_streams *streams,
		struct niohttpd_stream_entry *entry)
{
	TAILQ_REMOVE(&streams->list, &entry->stream, next);
	RB_REMOVE(niohttpd_stream_tree, &streams->tree, entry);

	niohttpd_clients_clear(&entry->clients);

	if (entry->headers) {
		niohttp_headers_free(entry->headers);
	}

	if (entry->uri) {
		free(entry->uri);
	}

	free(entry);
}

void niohttpd_streams_clear(struct niohttpd_streams *streams)
{
	struct niohttpd_stream_entry *entry;

	while ((entry = RB_MIN(niohttpd_stream_tree, &streams->tree))) {
		niohttpd_streams_remove(streams, entry);
	}
}

int niohttpd_stream_entry_compare(struct niohttpd_stream_entry *a,
		struct niohttpd_stream_entry *b)
{
	return (strcasecmp(a->uri, b->uri));
}

RB_GENERATE(niohttpd_stream_tree, niohttpd_stream_entry, entry,
		niohttpd_stream_entry_compare);
